package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.example.lotterysystem.controller.param.CreateUserByActivityParam;
import com.example.lotterysystem.controller.param.PageParam;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;


import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @auther falls_vc
 * description:
 * @date 2025/5/28  19:43
 */
@Service
public class ActivityServiceImpl implements ActivityService {

    /**
     * 活动缓存前置，为了区分业务
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    private static final Logger logger= LoggerFactory.getLogger(ActivityServiceImpl.class);
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        // 1. 检查活动信息是否正确
        checkActivityInfo(param);

        // 2. 保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        // 3. 保存相关奖品信息
        List<CreatePrizeByActivityParam> prizeParams=param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList =prizeParams.stream()
                .map(prizeParam->{
                    ActivityPrizeDO activityPrizeDO=new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        // 4. 保存相关用户信息
        List<CreateUserByActivityParam> userParams=param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList=userParams.stream()
                .map(userParam->{
                    ActivityUserDO activityUserDO=new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);
        // 5.整合完整的活动信息，存放 redis
        // activityId: ActivityDetailDTO:活动+奖品+人员
        // 6.先获取奖品基本属性列表
        // 获取需要查询的奖品id
        List<Long> prizeIds= param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList=prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetailDTO activityDetailDTO= convertToActivityDetailDTO(activityDO,prizeDOList,activityPrizeDOList,activityUserDOList);
        // 缓存活动信息
        cacheActivity(activityDetailDTO);

        // 7. 构造返回
        CreateActivityDTO createActivityDTO=new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;

    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        // 获取总量
        int total=activityMapper.count();
        // 获取当前页列表
        List<ActivityDO> activityDOList=activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList=activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO=new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<ActivityDTO>(total,activityDTOList);

    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null == activityId) {
            logger.warn("查询活动详细信息失败，activityId为null");
            return null;
        }
        //  查询redis

        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if(null != detailDTO) {
            logger.info("查询活动详细信息成功！ detailDTO：{}",JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }
        // 为null
        // 如果redis不存在 查表
        // 活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);
        // 活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);

        // 活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        // 奖品信息
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        // 整合信息存放redis
        detailDTO = convertToActivityDetailDTO(activityDO, pDOList, apDOList, auDOList);
        cacheActivity(detailDTO);

        return detailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId) {
            logger.warn("要缓存的id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        // 活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);

        if(null == activityDO) {
            logger.warn("要缓存的活动id有误！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        // 活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);

        // 活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        // 奖品信息
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        // 整合信息存放redis

        cacheActivity(convertToActivityDetailDTO(activityDO, pDOList, apDOList, auDOList));
    }

    /**
     * 缓存活动信息 ActivityDetailDTO
     * @param activityDetailDTO
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        // key : ACTIVITY_1
        // value : ActivityDetailDTO(json)
        if(null == activityDetailDTO || null ==activityDetailDTO.getActivityId()) {
            logger.warn("要缓存的信息不存在");
            return;
        }
        try{
            redisUtil.set(ACTIVITY_PREFIX+activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    ACTIVITY_TIMEOUT);

        }catch (Exception exception) {
            logger.error("缓存活动异常,ActivityDetailDTO:{}",JacksonUtil.writeValueAsString(activityDetailDTO),exception);
        }

    }
    /**
     * 根据活动id从缓存中获取活动详细信息
     *
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(null == activityId) {
            logger.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try{
            String str=redisUtil.get(ACTIVITY_PREFIX+activityId);
            if(!StringUtils.hasText(str)) {
                logger.info("获取的缓存活动数据为空！key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(str,ActivityDetailDTO.class);
        }catch (Exception exception) {
            logger.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX + activityId, exception);
            return null;

        }
    }
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList) {
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        List<ActivityDetailDTO.PrizeDTO> prizeDTOList=activityPrizeDOList
                        .stream()
                        .map(apDO->{
                            ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                            prizeDTO.setPrizeId(apDO.getPrizeId());

//                            Optional 类的封装类
                            Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                                                    .findFirst();
                            // prizeDO为空，不执行当前方法，不为空才执行
                            optionalPrizeDO.ifPresent(prizeDO->{
                                prizeDTO.setName(prizeDO.getName());
                                prizeDTO.setImageUrl(prizeDO.getImageUrl());
                                prizeDTO.setPrice(prizeDO.getPrice());
                                prizeDTO.setDescription(prizeDO.getDescription());
                            });

                            prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                            prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                            prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                            return prizeDTO;
                        }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);
        List<ActivityDetailDTO.UserDTO> userDTOList= activityUserDOList.stream()
                        .map(auDO->{
                            ActivityDetailDTO.UserDTO userDTO=new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(auDO.getUserId());
                            userDTO.setUserName(auDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                            return userDTO;
                        }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);

        return activityDetailDTO;
    }

    /**
     * 验证活动是否有效
     *
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(null == param) throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        // 检查人员id是否存在
        List<Long> userIds=param.getActivityUserList()
                .stream().map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds=userMapper.selectExistIdsByIds(userIds);

        if(CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id->{
            if(!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        // 奖品id 是否存在
        List<Long> prizeIds=param.getActivityPrizeList()
                .stream().map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        List<Long> existPrizeIds=prizeMapper.selectExistIdsByIds(prizeIds);

        if(CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(prizeId->{
            if(!existPrizeIds.contains(prizeId)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        // 人员数量是否大于奖品数量

        int userAmount=param.getActivityUserList().size();
        long prizeAmount=param.getActivityPrizeList().stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if( userAmount < prizeAmount ) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_PRIZE_AMOUNT_ERROR);
        }
        //判断奖品等级的有效性
        param.getActivityPrizeList().forEach(prize->{
            if(null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });


    }
}
